#include "RatingConvert.h"
#include <complex>
using namespace std;

enum STATS {
    AIM = 0, CUNNING, STRENGTH, WILLPOWER = 3,
    CRIT, SURGE, ALACRITY , POWER , ACCURACY = 8,
    DEFENSE, SHIELD, ABSORB, ARMOR = 12,
    LEVEL, MH_ACC, OH_ACC, ENEMY_LEVEL, ENEMY_ARMOR= 17,
    SPEED,  MH_MAX, MH_MIN, OH_MAX , OH_MIN =22,
    TECH_CRIT_CHANCE , TECH_CRIT_SIZE, TECH_BONUS_HEALING, TECH_BONUS_DAMAGE = 26,
    RANGED_CRIT_CHANCE , RANGED_CRIT_SIZE, RANGED_BONUS_DAMAGE = 29,
    FORCE_CRIT_CHANCE , FORCE_CRIT_SIZE, FORCE_BONUS_HEALING, FORCE_BONUS_DAMAGE = 33,
    MELEE_CRIT_CHANCE , MELEE_CRIT_SIZE, MELEE_BONUS_DAMAGE = 36,
    TECH_POWER, FORCE_POWER = 38
};

double Armor(double level, double base, double armor) {
    double armorDR = (armor / (armor + 200 * level + 800)*100 + base) / 100;
    return armorDR;
}

double Defense(double level, double base, double rating) {
    double defense = 0.30 * (1 - pow((1 - (0.01 / 0.3)), ((rating / max(level, 20.0)) / 0.55))) + (base) / 100;
    return defense;
}

double Shield(double level, double base, double rating, double genShield) {
    double shield = 0.50 * (1 - pow((1 - (0.01 / 0.5)), ((rating / max(level, 20.0)) / 0.32)))+ (genShield + base) / 100;
    return shield;
}

double Absorb(double level, double base, double rating, double genAbsorb) {
    double absorb = 0.50 * (1 - pow((1 - (0.01 / 0.5)), ((rating / max(level, 20.0)) / 0.18))) + (genAbsorb + base) / 100;
    return absorb;
}

double CritChance(int primaryStat, int secondaryStat, int rating, int skills, double level) {
    double critChance = 5 + skills
            + 30 * (1 - exp(-primaryStat / ((-(max(level, 20.0)*2.5)) / log(1 - (0.01 / 0.3)))))
            + 30 * (1 - pow((1 - (0.01 / 0.3)), ((secondaryStat / max(level, 20.0)) / 2.5)))
            + 30 * (1 - pow((1 - (0.01 / 0.3)), ((rating / max(level, 20.0)) / 0.45)));
    return critChance;
}

double CritSize(int rating, int skills, double level) {
    double critSize = skills + 50 + 30 * (1 - pow((1 - (0.01 / 0.3)), ((rating / max(level, 20.0)) / 0.11)));
    return critSize;
}

double Speed(int rating, int skills, double level) {
    double x = 0.3;
    double t = -max(level, 20.0)*0.55 / log((1 - 0.01 / x));
    double speed = (1 - skills / 100.0 - x * (1 - exp(-rating / t)));
    return speed;
}

double BonusHealing(int primaryStat, int power) {
    double bonusHealing = 0.14 * primaryStat + 0.17 * power;
    return bonusHealing;
}
double BonusDamage(int primaryStat, int power) {
    double bonusHealing = 0.2 * primaryStat + 0.23 * power;
    return bonusHealing;
}

double MH_Accuracy(double rating, double skills, double level) {

    double accuracy = skills + 90 + 30 * (1 - (1 - pow((0.01 / 0.3),((rating / max(level, 20.0)) / 0.55))));
    return accuracy;
}

double OH_Accuracy(double rating, double skills, double level)
{
   double accuracy = skills + 57 + 30 * (1 - (1 - pow((0.01 / 0.3),((rating / max(level, 20.0)) / 0.55))));
    return accuracy;
}


double TechCritChance(const vector <double>& stats, double skills, bool hasSecondary){
    double rating = stats[CRIT];
    double level = stats[LEVEL];
    double primaryStat = stats[CUNNING];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat = stats[AIM];}
    double critChance = CritChance(primaryStat, secondaryStat, rating, skills, level);
    return critChance;
}
double TechCritSize(const vector <double>& stats, double skills){
    double rating = stats[SURGE];
    double level = stats[LEVEL];
    double critSize = CritSize(rating, skills, level);
    return critSize;
}

double ForceCritChance(const vector <double>& stats, double skills, bool hasSecondary){
    double rating = stats[CRIT];
    double level = stats[LEVEL];
    double primaryStat = stats[WILLPOWER];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat = stats[STRENGTH];}
    double critChance = CritChance(primaryStat, secondaryStat, rating, skills, level);
    return critChance;
}
double ForceCritSize(const vector <double>& stats, double skills){
    double rating = stats[SURGE];
    double level = stats[LEVEL];
    double critSize = CritSize(rating, skills, level);
    return critSize;
}

double RangedCritChance(const vector <double>& stats, double skills, bool hasSecondary){
    double rating = stats[CRIT];
    double level = stats[LEVEL];
    double primaryStat = stats[AIM];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat = stats[CUNNING];}
    double critChance = CritChance(primaryStat, secondaryStat, rating, skills, level);
    return critChance;
}
double RangedCritSize(const vector <double>& stats, double skills){
    double rating = stats[SURGE];
    double level = stats[LEVEL];
    double critSize = CritSize(rating, skills, level);
    return critSize;
}

double MeleeCritChance(const vector <double>& stats, double skills, bool hasSecondary){
    double rating = stats[CRIT];
    double level = stats[LEVEL];
    double primaryStat = stats[STRENGTH];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat = stats[WILLPOWER];}
    double critChance = CritChance(primaryStat, secondaryStat, rating, skills, level);
    return critChance;
}
double MeleeCritSize(const vector <double>& stats, double skills){
    double rating = stats[SURGE];
    double level = stats[LEVEL];
    double critSize = CritSize(rating, skills, level);
    return critSize;
}

double Speed(const vector <double>& stats, double skills){
    double rating = stats[ALACRITY];
    double level = stats[LEVEL];
    double x = 0.3;
    double t = -max(level, 20.0)*0.55 / log((1 - 0.01 / x));
    double speed = (1 - skills / 100.0 - x * (1 - exp(-rating / t)));
    return speed;
}


double TechBonusHealing(double power, double primaryStat){
    double bonusHealing = 0.14 * primaryStat + 0.17 * power;
    return bonusHealing;
}

double ForceBonusHealing(double power, double willpower){
    double bonusHealing = 0.14 * willpower + 0.17 * power;
    return bonusHealing;
}

double TechBonusDamage(const vector <double>& stats){
    double power = stats[POWER]+ stats.at(TECH_POWER);
    double primaryStat = stats[CUNNING];
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double ForceBonusDamage(const vector <double>& stats){
    double power = stats[POWER]+ stats.at(FORCE_POWER);
    double primaryStat = stats[WILLPOWER];
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double RangedBonusDamage(const vector <double>& stats){
    double power = stats[POWER];
    double primaryStat = stats[AIM];
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double MeleeBonusDamage(const vector <double>& stats){
    double power = stats[POWER];
    double primaryStat = stats[STRENGTH];
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double TechBonusDamage(const vector <double>& stats, bool hasSecondary){
    double power = stats[POWER]+ stats.at(TECH_POWER);
    double primaryStat = stats[CUNNING];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat=stats[AIM];}
    primaryStat += secondaryStat;
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double ForceBonusDamage(const vector <double>& stats, bool hasSecondary){
    double power = stats[POWER] + stats.at(FORCE_POWER);
    double primaryStat = stats[WILLPOWER];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat=stats[STRENGTH];}
    primaryStat += secondaryStat;
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double RangedBonusDamage(const vector <double>& stats, bool hasSecondary){
    double power = stats[POWER];
    double primaryStat = stats[AIM];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat=stats[CUNNING];}
    primaryStat += secondaryStat;
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double MeleeBonusDamage(const vector <double>& stats, bool hasSecondary){
    double power = stats[POWER];
    double primaryStat = stats[STRENGTH];
    double secondaryStat = 0;
    if(hasSecondary == true){secondaryStat=stats[WILLPOWER];}
    primaryStat += secondaryStat;
    double bonusDamage = BonusDamage(primaryStat, power);
    return bonusDamage;
}

double MH_Accuracy(const vector <double>& stats, double skills){
    double rating = stats[ACCURACY];
    double level = stats[LEVEL];
    double accuracy = skills + 100 + 30 * (1 - pow((1 - (0.01 / 0.3)),((rating / max(level, 20.0)) / 0.55)));
    return accuracy;
}
double OH_Accuracy(const vector <double>& stats, double skills){
    double level = stats[LEVEL];
    double rating = stats[ACCURACY];
    double accuracy = skills + 67 + 30 * (1 - pow((1 - (0.01 / 0.3)),((rating / max(level, 20.0)) / 0.55)));
    return accuracy;
}
